#region Terms of Usage
/*
 * Copyright 2006 Sameera Perera
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;
using Db4objects.Db4o;
using needle.Services.soab.Data;
using System.Reflection;

namespace needle.Services.soab.Cache
{
    public class CacheManager
    {
        private ReaderWriterLock cacheMgrLock;

        /// <summary>
        /// Occurs when an object is added to the cache or deleted/updated from the cache.
        /// </summary>
        public event EventHandler CacheUpdated;

        /// <summary>Removes the specified object from cache.</summary>
        /// <param name="data">The entry to be removed from the cache.</param>
        public void ClearCacheEntry( object data )
        {
            Database.Instance.Delete( data );
            if ( this.CacheUpdated != null )
                this.CacheUpdated( data, EventArgs.Empty );
        }

        /// <summary>Writes the given data to the cache.</summary>
        /// <param name="data">Data to be written to the cache</param>
        public void WriteCacheEntry( object data )
        {
            Database.Instance.Save( data );
            if ( this.CacheUpdated != null )
                this.CacheUpdated( data, EventArgs.Empty );
        }

        /// <summary>Refreshes a cached object with the data contained in an updated version of
        /// it.</summary>
        /// <param name="cachedObject">The cached version of the object.</param>
        /// <param name="updatedObject">The updated version of the object.</param>
        public void RefreshCacheEntry( object cachedObject, object updatedObject )
        {
            Database        db = Database.Instance;
            IObjectContainer oc = db.RawDatabase;

            db.AcquireWriterLock();
            if ( cachedObject == null )
                oc.Set( updatedObject );
            else
            {
                oc.Ext().Bind( updatedObject, oc.Ext().GetID( cachedObject ) );
                oc.Set( updatedObject );
            }
            db.ReleaseWriterLock();

            if ( this.CacheUpdated != null )
                this.CacheUpdated( updatedObject, EventArgs.Empty );
        }

        /// <summary>Reads an object from the cache, that matches the given prototype.</summary>
        /// <param name="prototype">An object prototype to be fetched from the cache.</param>
        /// <returns>The object retreived from the cache or <c>null</c> if no match was found
        /// to the give prototype.</returns>
        /// <remarks>This method returns only the first object when multiple entries are
        /// found matching the prototype. Use this only when finding collection objects. Otherwise,
        /// use of <see cref="GetCachedEntries{T}"/> is prefered.
        /// </remarks>
        public object ReadCacheEntry( object prototype )
        {
            return Database.Instance.GetObject( prototype );
        }

        /// <summary>Returns an array of objects from the cache.</summary>
        /// <typeparam name="T">The type of objects to be retreived.</typeparam>
        /// <returns>An array of objects of the specified type.</returns>
        public T[] FindCachedItems<T>()
        {
            return Database.Instance.FindObjects<T>();
        }

        private CacheManager()
        {
            this.cacheMgrLock       = new ReaderWriterLock();
        }

        private static CacheManager instance;
        public static CacheManager Instance
        {
            get 
            {
                lock ( typeof( CacheManager ) )
                {
                    if ( instance == null )
                        instance = new CacheManager();
                }
                return instance; 
            }
        }
    }
}
